// GRUPO 02 - TURMA A
// Gabriel Tessaroli	 – 4321350
// Lucas Nunes Arvani	 – 7986802
// Lucas Fúrio Franco  – 6793010

#include <iostream>
#include <sys/time.h>
#include <cstdlib>
#include <cstdio>
#include <string.h>

using namespace std;

//Headers
unsigned char *imsmooth(unsigned char *image, int N, int cols, int rows);
int split_ppm(unsigned char*** Channels, int *cols, int *rows, int *max);
void write_ppm(unsigned char** Channels, int cols, int rows, int max);
void print_time(struct timeval begin, struct timeval end, int flag);

int main(int argc, char *argv[]) {

	int param = atoi(argv[1]);
	struct timeval b_prog, e_prog;
	struct timeval b_proc, e_proc;

	// verefica se paramentro é impar.
	if (!(param & 1)) {
		cout << "smooth_param must be even." << endl;
		return -2;
	}

	// calculo do tempo.
	gettimeofday(&b_prog, NULL);

	unsigned char **Channels = new unsigned char*[3];
	int cols, rows;
	int max;

	// abre imagem, coleta dados divididos por canais rgb.
	split_ppm(&Channels, &cols, &rows, &max);

	gettimeofday(&b_proc, NULL);

	// realiza o processamento da imagem.
	unsigned char **Channels_ = new unsigned char*[3];
	Channels_[0] = imsmooth(Channels[0], param, cols, rows);
	Channels_[1] = imsmooth(Channels[1], param, cols, rows);
	Channels_[2] = imsmooth(Channels[2], param, cols, rows);

	gettimeofday(&e_proc, NULL);

	// escreve em um arquivo de saida a imagem final.
	write_ppm(Channels_, cols, rows, max);

	gettimeofday(&e_prog, NULL);

	// libera memória.
	for (int i = 0; i < 3; ++ i) delete Channels[i];
	delete Channels;
	for (int i = 0; i < 3; ++ i) delete Channels_[i];
	delete Channels_;

	// calculo do tempo.
	print_time(b_prog, e_prog, 1);
	print_time(b_proc, e_proc, 0);

	return 0;
}

// escreve arquivo da imagem final
void write_ppm(unsigned char** Channels, int  cols, int rows, int max) {

	FILE *out = fopen("out.ppm", "w");

	// escreve informações iniciais necessarias.
	fprintf(out, "P3\n");
	fprintf(out, "%d %d\n%d", cols, rows, max);
	int p = 0;
	// escreve pixels no arquivo.
	for(int i = 0; i < cols; ++i) {
		for(int j = 0; j < rows; ++j, ++p) {
			if (!(j % 6)) fprintf(out, "\n");
			fprintf(out, "%d %d %d ", Channels[0][p], Channels[1][p], Channels[2][p]);
		}
	}
}

// Abre uma imagem e coleta seus dados.
int split_ppm(unsigned char*** Channels, int  *cols, int *rows, int *max) {

	char key[4];
	int r, g, b;
	scanf("%s", key);

	if (strcmp(key,"P3")) {
		cout << "Erro na leitura do arquivo .ppm." << endl;
		return -1;
	}

	// recebe valores da quantidade de colunas linhas e maximo
	scanf("%d %d %d", cols, rows, max);
	for(int i = 0; i <3 ; ++i) Channels[0][i] = new unsigned char[(*cols)*(*rows)];

	// coleta pixels dos canais rgb
	long long int p = 0;
	for(int i = 0; i < (*cols); ++i)
		for(int j = 0; j < (*rows); ++j, ++p) {
			scanf("%d %d %d", &r, &g, &b);
			Channels[0][0][p] = (unsigned char)r;
			Channels[0][1][p] = (unsigned char)g;
			Channels[0][2][p] = (unsigned char)b;
		}

	return 1;
}

// realização do algoritmo.
unsigned char *imsmooth(unsigned char *image, int N, int cols, int rows) {

	int sum;
	int avg;
	int border = N / 2;
	unsigned char *smooth = new unsigned char[cols*rows];

	// para cada pixel calcula o algoritmo de smooth
	for(int i = 0; i < rows; ++i) {
		for(int j = 0; j < cols; ++j) {
			sum = 0;

			for(int p = (i-border); p <= (i+border); ++p)
				for(int q = (j-border); q <= (j+border); ++q)
					// ternario com condição de pixels nas boardas da imagem.
					sum += (p>=0&&p<rows&&q>=0&&q<cols)? image[(p*cols) + q] : 0;

			avg = sum / (N*N);
			smooth[(i*cols) + j] = avg; //guarda resultados finais.
		}
	}

	return smooth;
}

// pega tempo
int getTime(void) {
	struct tm *l;
	time_t t = time(NULL);
	l = localtime(&t);
	return (l->tm_hour*3600 + l->tm_min*60 + l->tm_sec);
}

// imprime tempo
void print_time(struct timeval begin, struct timeval end, int flag) {
	if (flag) {
		printf("Program Time: ");
	} else {
		printf("Execution time: ");
	}

	if (end.tv_usec < begin.tv_usec) {
		printf("%ld.%06lds.\n", end.tv_sec - begin.tv_sec - 1, (-1)*(end.tv_usec - begin.tv_usec));
	} else {
		printf("%ld.%06lds.\n", end.tv_sec - begin.tv_sec, (end.tv_usec - begin.tv_usec));
	}
}
